package com.app.framework.orm;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;

import com.app.cache.CacheClient;
import com.app.framework.annotations.TransferInvisible;
import com.app.framework.annotations.Transient;
import com.app.framework.exception.JSONException;
import com.app.framework.exception.ORMException;
import com.app.framework.orm.session.PersistenceSession;
import com.app.framework.orm.session.PersistenceSessionAware;
import com.app.framework.orm.util.ClassHelper;
import com.app.framework.util.JSONUtil;
import com.app.framework.util.ReflectionUtil;
import com.app.framework.util.helper.ClassInfo;
import com.app.framework.util.helper.FieldInfo;

public class StreamHelper {
	
	private static final Logger logger = Logger.getLogger(StreamHelper.class);

	private static StreamHelper instance = new StreamHelper();
	
	private StreamHelper() {
		
	}
	
	public static StreamHelper getInstance() {
		return instance;
	}
	
	/**
	 * 模型到json转换
	 * @param clz
	 * @param entity
	 * @param key
	 * @param indexName
	 * @param indexType
	 * @param indexValue
	 * @return
	 * @throws ORMException
	 */
	public Map<String,Object> object2json(StorableModel entity,List<String> indexName,List<String> indexValue,LinkedHashMap<String, String> changed) throws ORMException  {
		
		ClassInfo classInfo=ReflectionUtil.getClassInfo(entity.getClass());
		if(classInfo == null) {
			throw new ORMException("class信息为空");
		}
		FieldInfo fieldInfo = null;
		Field field=null;
		Object fieldValueObject=null;
		String fieldValue=null;
		
		try{
			Map<String,FieldInfo> indexMap=classInfo.getIndexFields();
			if(indexMap != null && indexName!=null && indexValue != null ) {
				for(String fieldName:indexMap.keySet()) {
					field = indexMap.get(fieldName).getField();
					field.setAccessible(true);
					fieldValueObject = field.get(entity);
					if(fieldValueObject != null) {
						fieldValue = fieldValueObject.toString();
						indexValue.add(fieldValue);
						indexName.add(fieldName);
					}
				}
			}
		} catch (Exception e) {
			logger.error(e);
			e.printStackTrace();
			throw new ORMException(e);
		}
		
		Map<String,Object> resultMap = new LinkedHashMap<String, Object>();
		Map<String,FieldInfo> persistMap=classInfo.getPersistFields();
		
		boolean iscglibproxy = ClassHelper.isCglibProxyClass(entity.getClass());
		
		try{
			if(changed != null) {
				//放入id
				changed.put(classInfo.getIdField().getField().getName(), classInfo.getIdField().getField().get(entity).toString());
			}
			
			for(String fieldName:persistMap.keySet()) {
				fieldInfo = persistMap.get(fieldName);
				field = fieldInfo.getField();
				boolean fieldChanged = entity.isChanged(field);
				Object fieldValObj = field.get(entity);
				//reference的如果是map类型 则fieldvalobj是有值的
				//如果是list的 则fieldvalobj是null
				if(fieldInfo.isByReference()) {
					//从数据库中读出来的已经json序列化的东西
					if(iscglibproxy) {
						Map<String,?> referenceJson = ((ActiveRecord)entity).referenceJson;
						if(referenceJson.size() > 0) {
							String getMethodName = ClassHelper.genGetMethodNameByField(field);
							if(referenceJson.containsKey(getMethodName)) {
								Object ret = ((List<?>)referenceJson.get(getMethodName)).get(1);
								resultMap.put(fieldName, ret);
								if(changed != null && !iscglibproxy || fieldChanged) {
									String jsonvalue = JSONUtil.toJSON(ret);
									
									changed.put(fieldName,  jsonvalue== null ? CacheClient.CACHE_RECORD_NULL_VALUE : jsonvalue);
								}
								continue;
							}
						}
					}
					List<?> tmp = saveReferenceObject2Json(fieldValObj);
					if(tmp != null) {
						resultMap.put(fieldName, tmp);
					}
				}
				else {
					if(fieldValObj == null) {
						continue;
					}
					Class<?> fieldType = fieldValObj.getClass();
					if(ClassHelper.isSimpleClass(fieldType)) {
						resultMap.put(fieldName, fieldValObj);
					}
					else if(fieldInfo.isJsonableCollection()){
						resultMap.put(fieldName, fieldValObj);
					}
					else if(fieldInfo.isJsonableArray()) {
						resultMap.put(fieldName, JSONUtil.toJSON(fieldValObj));
					}
					else {
						if(fieldInfo.isByValue()) {
							List<?> tmp = saveValueObject2Json(fieldValObj);
							if(tmp != null) {
								resultMap.put(fieldName, tmp);
							}
						}else {
							byte[] outByte = this.object2bytes(fieldValObj);
							String value = Base64.encodeBase64String(outByte);
							resultMap.put(fieldName, value);
							if(logger.isDebugEnabled()) {//如果在debug模式下
								logger.debug("使用byte方式转换对象 className:"+entity.getClass().getName() +" fieldName:"+fieldName);
							}
						}
					}
				}
				
				if(changed != null && !iscglibproxy || fieldChanged) {//对应的值若为null也需要放进去
					String jsonvalue = JSONUtil.toJSON(resultMap.get(fieldName));
					changed.put(fieldName, jsonvalue== null ? CacheClient.CACHE_RECORD_NULL_VALUE : jsonvalue);
				}
				
			}
		}catch (Exception e) {
			logger.error("模型到json转换中获取对象存储属性值出错 class:"+entity.getClass().getName(),e);
			e.printStackTrace();
			throw new ORMException(e);
		} 
		return resultMap;
	}
	
	/**
	 * 不能json化的数据写成byte数组
	 * @param entity
	 * @return
	 */
	public byte[] object2bytes(Object entity) throws ORMException {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ObjectOutputStream oos=null;
		try {
			oos=new ObjectOutputStream(bos);
			oos.writeObject(entity);
		} catch (Exception e) {
			logger.error("模型到byte数组转换出错 class:"+entity.getClass().getName(),e);
			e.printStackTrace();
			throw new ORMException(e);
		}
		byte[] outBytes= bos.toByteArray();
		return outBytes;
	}
	/**
	 * byte数组转到对象(较耗资源)
	 * @param bytes
	 * @return
	 */
	public Object bytes2object(byte[] bytes){
		Object object = null;
		try {
			ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
			ObjectInputStream ois = null;
			ois = new ObjectInputStream(bis);
			object = ois.readObject();
		} catch (Exception e) {
			logger.error(e);
			e.printStackTrace();
		}
		return object;
	}
	
	/**
	 * 
	 * @param jsonMap
	 * @param clz
	 * @return
	 * @throws ORMException
	 * @throws JSONException 
	 */
	public StorableModel json2Object(Map<String,?> jsonMap,Class<StorableModel> clz,PersistenceSession persistenceSession) throws ORMException {
		if(jsonMap == null) {
			return null;
		}
		ClassInfo classInfo = ReflectionUtil.getClassInfo(clz);
		
		Map<String,FieldInfo> fieldInfoMap = classInfo.getPersistFields(); 
		
		FieldInfo fieldInfo = null;
		Field field = null;
		Class<?> fieldType = null;
		StorableModel entity = generateEntity(jsonMap, clz, fieldInfoMap,persistenceSession);
		try {
			for(String fieldName : fieldInfoMap.keySet()) {
				if(jsonMap.containsKey(fieldName)) {
					fieldInfo = fieldInfoMap.get(fieldName);
					field = fieldInfo.getField();
					field.setAccessible(true);
					fieldType = fieldInfo.getField().getType();
					if(ClassHelper.isSimpleClass(fieldType)) {
						field.set(entity, ClassHelper.getSimpleClassValue(jsonMap, fieldName, fieldType));
					}
					else if(fieldInfo.isJsonableCollection()) {
						field.set(entity, jsonMap.get(fieldName));
					}
					else if(fieldInfo.isJsonableArray()) {
						field.set(entity, JSONUtil.toObject(jsonMap.get(fieldName).toString(), field.getType()));
					}
					else {
						if(fieldInfo.isByReference()) {
							if(ClassHelper.isMap(fieldType)) {//加载map中的所有key
								Object jsonvalue = jsonMap.get(fieldName);
								if(String.class.isAssignableFrom(jsonvalue.getClass())) {
									jsonvalue = JSONUtil.toList((String)jsonvalue);
								}
								field.set(entity, getReferenceMapKeys((List<?>) jsonvalue));
							}
						}
						else if(fieldInfo.isByValue()) {
							Object jsonvalue = jsonMap.get(fieldName);
							if(String.class.isAssignableFrom(jsonvalue.getClass())) {
								jsonvalue = JSONUtil.toList((String)jsonvalue);
							}
							field.set(entity, saveValueJson2Object((List<?>) jsonvalue, fieldType,persistenceSession));
						}
						else {
							String fieldValue = jsonMap.get(fieldName).toString();
							 byte[] bytes = Base64.decodeBase64(fieldValue);
							field.set(entity, bytes2object(bytes));
						}
					}
				}
			}
		} catch (Exception e) {
			logger.error("json到模型转换中对属性赋值出错");
			e.printStackTrace();
			throw new ORMException(e);
		}
		
		return entity;
	}
	/**
	 * 生成cglib代理类
	 * @param jsonMap
	 * @param clz
	 * @param fieldInfoMap
	 * @param persistenceSession 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private StorableModel generateEntity(Map<String,?> jsonMap,Class<StorableModel> clz,Map<String,FieldInfo> fieldInfoMap, PersistenceSession persistenceSession) {
		StorableModel entity = null;
		ReferenceInterceptor interceptor = new ReferenceInterceptor(persistenceSession);
		Enhancer en = new Enhancer();
		en.setSuperclass(clz);
		en.setCallback(interceptor);
		entity = (StorableModel) en.create();
		ActiveRecord record = (ActiveRecord)entity;
		//设置引用
		for(String fieldName : fieldInfoMap.keySet()) {
			FieldInfo fieldInfo = fieldInfoMap.get(fieldName);
			if(fieldInfo.isByReference()) {
				List<Object> referencejson = new ArrayList<Object>();
				if(!jsonMap.containsKey(fieldName)) {
					continue;
				}
				referencejson.add(fieldInfo.getField());
				Object fieldValue = jsonMap.get(fieldName);
				if(String.class.isAssignableFrom(fieldValue.getClass())) {
					fieldValue = JSONUtil.toList((String)fieldValue);
				}
				List<Object> lst = (List<Object>) fieldValue;//list的第一项为classname其余项为对象uid或者为map
				referencejson.add(lst);
				record.referenceJson.put(ClassHelper.genGetMethodNameByField(fieldInfo.getField()), referencejson);
 			}
		}
		
		if(PersistenceSessionAware.class.isAssignableFrom(clz)) {
			PersistenceSessionAware aware = (PersistenceSessionAware) entity;
			aware.setPersistenceSession(persistenceSession);
		}
		
		entity.notModified_set();
		return entity;
	}
	/**
	 * 根据class和list uid或者map uid获取数据
	 * @param jsonList
	 * @return
	 * @throws ORMException
	 */
	@SuppressWarnings("unchecked")
	private Map<Object, StorableModel> getResultByReference(List<?> jsonList,Class<?> classType,PersistenceSession persistenceSession) throws  ORMException {
		List<Long> keyList = new ArrayList<Long> ();
		String className = jsonList.get(0).toString();
		Map<Object,StorableModel> resultMap = new LinkedHashMap<Object,StorableModel>();
		Map<Object,Long> referenceMap = null;
		if(ClassHelper.isList(classType)) {
			for(int i=1;i<jsonList.size();i++) {
				keyList.add(Long.parseLong(jsonList.get(i).toString()));
			}
		}
		else if(ClassHelper.isMap(classType)) {
			referenceMap = (Map<Object, Long>) jsonList.get(1);
			keyList.addAll(referenceMap.values());
		}
		else if(ClassHelper.isRecord(classType)) {
			keyList.add(Long.parseLong(jsonList.get(1).toString()));
		}
		
		try {
			Map<Long, StorableModel> tmp = (Map<Long, StorableModel>) persistenceSession.get(keyList,  (Class<StorableModel>)Class.forName(className));
			if(referenceMap != null) {
				for(Entry<Object, Long> e : referenceMap.entrySet()) {
					StorableModel val = tmp.get(e.getValue());
					resultMap.put(e.getKey(), val);
				}
			}
			else {
				resultMap.putAll(tmp);
			}
		} catch (Exception e) {
			logger.error("get result by referenct error"+e.getMessage(),e);
			e.printStackTrace();
			throw new ORMException(e);
		}
		return resultMap;
	}
	/**
	 * 获取reference类型的数据
	 * @param jsonList
	 * @param fieldType
	 * @return
	 * @throws ORMException
	 * @throws JSONException
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Object saveReferenceJson2Object (List<?> jsonList,Class<?> fieldType,PersistenceSession persistenceSession) {
		if(jsonList == null) {
			return null;
		}
		Object obj = null;
		try {
			Map<Object,StorableModel> resultMap = this.getResultByReference(jsonList,fieldType,persistenceSession);
			
			if(ClassHelper.isList(fieldType)) {
				obj = new ArrayList(resultMap.values());
			}
			if(ClassHelper.isMap(fieldType)) {
				return resultMap;
			}
			if(ClassHelper.isRecord(fieldType)) {
				for(Object tmp : resultMap.values()) {
					obj = tmp;
					return obj;
				}
			}
		}catch (Exception e) {
			e.printStackTrace();
			logger.error("json到模型属性转换出错",e);
		}
		
		return obj;
	}
	
	@SuppressWarnings("unchecked")
	public Map<Object,Object> getReferenceMapKeys (List<?> jsonList) {
		if(jsonList == null) {
			return null;
		}
		Map<Object,Object> map = new LinkedHashMap<Object, Object>();
		
		Map<Object,String> tmp = (Map<Object, String>) jsonList.get(1);
		for(Object key : tmp.keySet()) {
			map.put(key, null);
		}
		return map;
	}
	
	/**
	 * 转换save reference对象 到json串
	 * @param fieldValue
	 * @return
	 * @throws ORMException
	 */
	@SuppressWarnings("unchecked")
	public List<?> saveReferenceObject2Json(Object fieldValue) throws ORMException {
		if(fieldValue == null) {
			return null;
		}
		List<Object> result = new ArrayList<Object>();
		Class<?> clz = fieldValue.getClass();
		if(ClassHelper.isList(clz)) {
			List<StorableModel> lst = (List<StorableModel>)fieldValue;
			if(lst.isEmpty()) {
				return null;
			}
			String classname = null;
			for(StorableModel o : lst) {
				if(o == null) {
					continue;
				}
				if(classname == null) {
					classname = o.getClassname();
					result.add(classname);
				}
				if(!classname.equals(o.getClassname())) {
					throw new ORMException("集合中的元素类型不一致 :"+classname+" -- "+o.getClassname());
				}
				result.add(o.getId());
			}
		}
		else if(ClassHelper.isMap(clz)) {
			Map<?,StorableModel> map = (Map<?,StorableModel>)fieldValue;
			if(map.isEmpty()) {
				return null;
			}
			Map<Object,Long> tmp = new LinkedHashMap<Object,Long>();
			String classname = null;
			for(Entry<?, StorableModel> e : map.entrySet()) {
				if(e.getKey() == null || e.getValue() == null) {
					continue;
				}
				if(classname == null) {
					classname = e.getValue().getClassname();
					result.add(classname);
				}
				if(!classname.equals(e.getValue().getClassname())) {
					throw new ORMException("集合中的元素类型不一致 :"+classname+" -- "+e.getValue().getClassname());
				}
				tmp.put(e.getKey(), e.getValue().getId());
			}
			result.add(tmp);
		}
		else if(ClassHelper.isRecord(clz)) {
			result.add(((StorableModel)fieldValue).getClassname());
			result.add(((StorableModel)fieldValue).getId());
		}
		else {
			logger.error("未支持的引用属性类型:"+fieldValue.getClass().getName());
			throw new ORMException("未支持的引用属性类型:"+fieldValue.getClass().getName());
		}
		return result;
	}
	
	/**
	 * 转换save value对象到json串
	 * @param fieldValue
	 * @return
	 * @throws ORMException
	 */
	@SuppressWarnings("unchecked")
	public List<?> saveValueObject2Json(Object fieldValue) throws ORMException {
		List<Object> result = new ArrayList<Object>();
		Class<?> clz = fieldValue.getClass();
		if(ClassHelper.isList(clz)) {
			List<StorableModel> lst = (List<StorableModel>)fieldValue;
			if(lst.isEmpty()) {
				return null;
			}
			String classname = null;
			for(StorableModel o : lst) {
				if(o == null) {
					continue;
				}
				if(classname == null) {
					classname = o.getClassname();
					result.add(classname);
				}
				if(!classname.equals(o.getClassname())) {
					throw new ORMException("集合中的元素类型不一致 :"+classname+" -- "+o.getClassname());
				}
				result.add(object2json(o, null, null, null));
			}
		}
		else if(ClassHelper.isMap(clz)) {
			Map<?,StorableModel> map = (Map<?,StorableModel>)fieldValue;
			Map<Object,Object> tmp = new LinkedHashMap<Object,Object>();
			if(map.isEmpty()) {
				return null;
			}
			String classname = null;
			for(Entry<?, StorableModel> e : map.entrySet()) {
				StorableModel val = e.getValue();
				Object key = e.getKey();
				if(val == null || key == null) {
					continue;
				}
				if(classname == null) {
					classname = val.getClassname();
					result.add(classname);
				}
				if(!classname.equals(val.getClassname())) {
					throw new ORMException("集合中的元素类型不一致 :"+classname+" -- "+val.getClassname());
				}
				tmp.put(key, object2json(val, null, null, null));
			}
			result.add(tmp);
		}
		else if(ClassHelper.isRecord(clz)) {
			StorableModel record = (StorableModel) fieldValue;
			result.add(record.getClassname());
			result.add(object2json(record, null, null, null));
		}
		else {
			logger.error("未支持的引用属性类型:"+fieldValue.getClass().getName());
			throw new ORMException("未支持的引用属性类型:"+fieldValue.getClass().getName());
		}
		return result;
	}
	
	@SuppressWarnings("unchecked")
	public Object saveValueJson2Object(List<?> jsonList,Class<?> fieldType,PersistenceSession persistenceSession) {
		if(jsonList == null) { 
			return null;
		}
		Object obj = null;
		String className = jsonList.get(0).toString();
		Class<StorableModel> clz = null;
		try {
			clz = (Class<StorableModel>) Class.forName(className);
		
			if(ClassHelper.isList(fieldType)) {
				obj = new ArrayList<StorableModel>();
				for(int i=1;i<jsonList.size();i++) {
					((List<StorableModel>)obj).add(json2Object((Map<String, ?>)jsonList.get(i), clz,persistenceSession));
				}
			}
			else if(ClassHelper.isMap(fieldType)) {
				obj = new LinkedHashMap<Object, StorableModel>();
				Map<Object, Object> map = (Map<Object, Object>) jsonList.get(1);
				for(Entry<?, ?> e : map.entrySet()) {
					((Map<Object,StorableModel>)obj).put(e.getKey(), json2Object((Map<String, ?>) e.getValue(), clz,persistenceSession));
				}
			}
			else if(ClassHelper.isRecord(fieldType)) {
				obj = json2Object((Map<String, ?>) jsonList.get(1), clz,persistenceSession);
			}
			else {
				logger.error("未支持的Save Value 属性类型 fieldType:"+fieldType.getName());
			}
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("加载save value类型出错,",e);
			return null;
		}
		return obj;
	}
	/**
	 * 级联存储reference数据
	 * @param entity
	 * @throws ORMException
	 */
	@SuppressWarnings("unchecked")
	public void cascadePut (StorableModel entity,PersistenceSession persistenceSession) throws ORMException {
		
		ClassInfo classInfo = ReflectionUtil.getClassInfo(entity.getClass());
		
		Map<String,FieldInfo> fieldInfoMap = classInfo.getPersistFields();
		FieldInfo fieldInfo = null;
		Field field = null;
		Class<?> fieldType = null;
		try {
			for(String fieldName : fieldInfoMap.keySet()) {
				fieldInfo = fieldInfoMap.get(fieldName);
				if(fieldInfo.isByReference()) {
					field = fieldInfo.getField();
					field.setAccessible(true);
					Object obj = field.get(entity);
					if(obj != null) {
						fieldType = field.getType();
						if(ClassHelper.isList(fieldType)) {
							List<StorableModel> lst = (List<StorableModel>) obj;
							persistenceSession.put(lst);
						}
						else if(ClassHelper.isMap(fieldType)) {
							Map<?,StorableModel> map = (Map<?, StorableModel>) obj;
							persistenceSession.put(map.values());
						}
						else if(ClassHelper.isRecord(fieldType)) {
							persistenceSession.put((StorableModel) obj);
						}
					}
				}
			}
		}catch (Exception e) {
			e.printStackTrace();
			logger.error(e);
			throw new ORMException(e);
		}  
	}
	
	@SuppressWarnings("unchecked")
	public void cascadeRemove (StorableModel entity,PersistenceSession persistenceSession) throws ORMException {
		
		ClassInfo classInfo = ReflectionUtil.getClassInfo(entity.getClass());
		
		List<Method> cascadeRemoveMethods = classInfo.getCascadeRemoveMethods();
		try {
			for(Method getMethod : cascadeRemoveMethods) {
				getMethod.setAccessible(true);
				Object obj = getMethod.invoke(entity, (Object[])null);//可能会通过cglib获取
				if(obj != null) {
					if(ClassHelper.isList(obj.getClass())) {
						List<StorableModel> lst = (List<StorableModel>) obj;
						persistenceSession.remove(lst);
					}
					else if(ClassHelper.isMap(obj.getClass())) {
						Map<?,StorableModel> map = (Map<?, StorableModel>) obj;
						persistenceSession.remove(map.values());
					}
					else if(ClassHelper.isRecord(obj.getClass())) {
						persistenceSession.remove((StorableModel) obj);
					}
				}
			}
		}catch (Exception e) {
			e.printStackTrace();
			logger.error(e);
			throw new ORMException(e);
		}  
		
	}
	
	/**
	 * 拦截reference属性的get方法
	 * @author lisong
	 * @version 2013-3-22 上午11:26:00
	 */
	class ReferenceInterceptor implements MethodInterceptor {
	
		@Transient
		@TransferInvisible
		private PersistenceSession persistenceSession;
		/**
		 * @param persistenceSession
		 */
		public ReferenceInterceptor(PersistenceSession persistenceSession) {
			this.persistenceSession =persistenceSession;
		}

		@Override
		public Object intercept(Object obj, Method method, Object[] args,
				MethodProxy proxy) throws Throwable {
			ActiveRecord record = (ActiveRecord)obj;
			String methodName = method.getName();
			if(record.referenceJson.containsKey(methodName)) {//调用所有引用类型属性的get方法
				//延迟加载
				List<?> referencejson = record.referenceJson.get(methodName); 
				 ((Field)referencejson.get(0)).set(obj, saveReferenceJson2Object((List<?>)referencejson.get(1), ((Field)referencejson.get(0)).getType(),persistenceSession));
				 record.referenceJson.remove(methodName);//移除 防止调用方法时重复加载
			}
			else {//调用所有属性的set方法
				if(methodName.length() > 3 && methodName.indexOf("set") == 0) {//以set开头的方法
					if(!record.changed.contains(methodName)) {
						record.changed.add(methodName);
					}
					record.modified = true;
				}
			}
			
			return proxy.invokeSuper(obj, args);
		}
	}
}
